#include "my_camera.h"
#include <errno.h>

pthread_mutex_t mutex;

#define HOST_IP "192.168.211.121"
#define HOST_PORT 6969

void camera_start()
{
    int fd;
    enum v4l2_buf_type type;
    struct v4l2_buffer buffer;
    pthread_mutex_init(&mutex, NULL);

    //开启服务器
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if(sockfd == -1)
        PERROR("socket");

    struct sockaddr_in ser;
    ser.sin_family = AF_INET;
    ser.sin_port = htons(HOST_PORT);  // uint16_t
    ser.sin_addr.s_addr = inet_addr(HOST_IP);  

    if(-1 == bind(sockfd, (struct sockaddr*)&ser, sizeof(ser)))
        PERROR("camsocket_bind");

    if(-1 == listen(sockfd, BACKLOG))
        PERROR("listen");

    printf("listen---------\n");

    // 1. 打开摄像头
    fd = open("/dev/video0", O_RDWR);
    if (-1 == fd)
    {
        PERROR("open");
    }

    struct v4l2_capability cap = {0};
    // 2. 提取摄像头的能力（linux v4l2规范）
    int ret = ioctl(fd, VIDIOC_QUERYCAP, &cap);
    if (-1 == ret)
    {
        PERROR("ioctl");
    }

    printf("driver name: %s   device name: %s device location: %s\n", cap.driver, cap.card, cap.bus_info);
    printf("version: %u.%u.%u\n", (cap.version >> 16) & 0xff, (cap.version >> 8) & 0xff, cap.version & 0xff);

    if (cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)
        printf("v4l2 dev support capture\n");

    if (cap.capabilities & V4L2_CAP_VIDEO_OUTPUT)
        printf("v4l2 dev support output\n");

    if (cap.capabilities & V4L2_CAP_VIDEO_OVERLAY)
        printf("v4l2 dev support overlay\n");

    if (cap.capabilities & V4L2_CAP_STREAMING)
        printf("v4l2 dev support streaming\n");

    if (cap.capabilities & V4L2_CAP_READWRITE)
        printf("v4l2 dev support read write\n");

    struct v4l2_fmtdesc fmtdesc;
    fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    fmtdesc.index = 0;
    //支持哪些图片格式
    while (!ioctl(fd, VIDIOC_ENUM_FMT, &fmtdesc))
    {
        printf("fmt:%s\n", fmtdesc.description);
        fmtdesc.index++;
    }

    // 3. 设置合适的采样方式
    struct v4l2_format v4l2_fmt;
    memset(&v4l2_fmt, 0, sizeof(struct v4l2_format));
    v4l2_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    v4l2_fmt.fmt.pix.width = W;                        //宽度
    v4l2_fmt.fmt.pix.height = H;                       //高度
    v4l2_fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG; //像素格式M-JPEG
    v4l2_fmt.fmt.pix.field = V4L2_FIELD_ANY;
    // v4l2_fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
    if (ioctl(fd, VIDIOC_S_FMT, &v4l2_fmt) < 0)
    {
        printf("ERR(%s):VIDIOC_S_FMT failed\n", __func__);
        return;
    }

    // 4. 如果支持STREAM则设置缓冲队列属性
    struct v4l2_requestbuffers req;
    req.count = QUESIZE; //缓存数量
    req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    req.memory = V4L2_MEMORY_MMAP;
    if (ioctl(fd, VIDIOC_REQBUFS, &req) < 0)
    {
        printf("ERR(%s):VIDIOC_REQBUFS failed\n", __func__);
        return;
    }

    //将内核中的4个缓冲区依次映射到用户缓冲区
    int i = 0;
    struct v4l2_buffer v4l2_buffer;
    for (; i < QUESIZE; i++)
    {
        memset(&v4l2_buffer, 0, sizeof(struct v4l2_buffer));
        v4l2_buffer.index = i; //想要查询的缓存
        v4l2_buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        v4l2_buffer.memory = V4L2_MEMORY_MMAP;

        /* 查询缓存信息 */
        ret = ioctl(fd, VIDIOC_QUERYBUF, &v4l2_buffer);
        if (ret < 0)
        {
            printf("Unable to query buffer.\n");
            return;
        }

        /* 得到图像缓冲位置 */
        //内存映射

        addr[i] = mmap(NULL, v4l2_buffer.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, v4l2_buffer.m.offset);
        v4l2_buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        v4l2_buffer.memory = V4L2_MEMORY_MMAP;

        if (ioctl(fd, VIDIOC_QBUF, &v4l2_buffer) < 0)
        {
            printf("ERR(%s):VIDIOC_QBUF failed\n", __func__);
            return;
        }
    }
#if 1
    // 5. 开启采样流
    type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    if (ioctl(fd, VIDIOC_STREAMON, &type) < 0)
    {
        printf("ERR(%s):VIDIOC_STREAMON failed\n", __func__);
        return;
    }

    // 6. 等待采样成功并提取图像(select)
    struct pollfd poll_fds[1];
    poll_fds[0].fd = fd;
    poll_fds[0].events = POLLIN; //关注可读
    poll(poll_fds, 1, 10000);    //等待有图像准备好
    while(1)
    {
loop_accept:
        printf("wait for accept\n");
        pic_fd = accept(sockfd, NULL, NULL);
        if (pic_fd == -1)
            PERROR("accept");
        printf("accept---success\n");



        while (1) //连拍
        {
            

            // 6.2 提取采样内容
            printf("*\n");
            buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
            buffer.memory = V4L2_MEMORY_MMAP;
            if (ioctl(fd, VIDIOC_DQBUF, &buffer) < 0)
            {
                printf("ERR(%s):VIDIOC_DQBUF failed, dropped frame\n", __func__);
                return;
            }

            // buffer.index： 成功拍摄出画面的下标
            // buffer.bytesused: 成功拍下来图片的大小
            // addr[buffer.index]: 图片存储的内存位置
            // printf("ready: %u %u\n", buffer.index, buffer.bytesused);

            //通过tcp将视频发送
            int len = buffer.bytesused;
            char buf_len[8] = {0};
            int count = 0;
            int ret;
            sprintf(buf_len, "%d", len);
            printf("len = %s\n", buf_len);
            //传输文件的大小
            printf("**\n");
            if (pic_fd == -1)
            {
                // close(fd);
                printf("over2\n");
                //sleep(1);
                goto loop_accept;
            }
            pthread_mutex_lock(&mutex);
            
            // printf("befor1\n");
            write(pic_fd, buf_len, sizeof(buf_len));
            // printf("after1\n");
            pthread_mutex_unlock(&mutex);
            //传输图像缓存
            printf("***\n");
            while (count < len)
            {
                if (pic_fd == -1)
                {
                    //close(fd);
                    printf("over3\n");
                    //sleep(1);
                    goto loop_accept;
                }
                pthread_mutex_lock(&mutex);
                // printf("befor2\n");
                ret = write(pic_fd, addr[buffer.index] + count, len - count);
                printf("ret = %d\n",ret);
                // printf("after2\n");
                pthread_mutex_unlock(&mutex);
                if (ret < len)
                {
                    // printf("-----数据太少------\n");
                    break;
                }
                count += ret;
            }
            count = 0;
            // 6.3 标识图像已经取走
            if (ioctl(fd, VIDIOC_QBUF, &buffer) < 0)
            {
                printf("ERR(%s):VIDIOC_QBUF failed\n", __func__);
                return;
            }
        }
    }
    
    //7. 关闭采样流
    if (ioctl(fd, VIDIOC_STREAMOFF, &type) < 0)
    {
        printf("ERR(%s):VIDIOC_STREAMOFF failed\n", __func__);
        return ;
    }
#endif
    //8. 关闭设备
    close(fd);
    close(sockfd);
    
    printf("camera---over----\n");
}
